package com.griddroid.nodes;

import java.io.IOException;
import java.net.UnknownHostException;

import android.os.Handler;
import android.os.Message;

import com.griddroid.configuration.GridConfig;
import com.griddroid.exceptions.UnknownComputationException;
import com.griddroid.messages.EndSessionAckMessage;
import com.griddroid.messages.PingMessage;
import com.griddroid.works.WorkRequest;
import com.griddroid.works.WorkResponse;

/**
 * This class represents a Grid Worker Node. It extends the abstract class
 * {@link GridNode GridNode} to gain the main functionality of.
 * 
 * @author Charalampos Mavroforakis
 * @author Thomas Ailianos
 * @author Michail Michailidis
 */
public class GridWorker extends GridNode
{
	/**
	 * Main function that constructs a GridWorker
	 * @param args
	 */
	public static void main(String args[])
	{
		GridWorker worker = new GridWorker(null);
		worker.start();
	}
	
	/**
	 * Creates a GridClient with the appropriate Handler to communicate with the
	 * activity. It also starts the the communication with the server.
	 * 
	 * @param msghandler
	 *            Handler to communicate with the activity.
	 */
	public GridWorker(Handler msghandler)
	{
		this.messageHandler = msghandler;
		this.type = NodeType.WORKER;
		this.log("Info", "Worker started");
		try
		{
			this.connectToServer();
		}
		catch (UnknownHostException e)
		{
			this.log("Connection error", "Could not connect to server. Unknown host!");
			System.exit(0);
		}
		catch (IOException e)
		{
			this.log("Connection error", "Could not connect to server. IOException!" + e.getMessage());
			System.exit(0);
		}
	}
	
	/**
	 * Starts the worker. Actually is a loop checking for timeouts, 
	 * receiving all available messages ({@link PinkMessage},{@link EndSessionAckMessage EndSessionAckMessage}).
	 * It also waits for a {@link WorkRequest WorkRequest} and then process it.
	 * 
	 */
	@Override
	public void start()
	{
		while (true)
		{
			// Check if worker must close
			if (this.forceExit)
				return;

			// Check if server is alive
			this.checkCounter++;
			this.checkForTimeout();

			// Check for new messages and handle them
			Object temp = null;
			try
			{
				while (this.handler.getInputStream().available() > 0)
				{
					temp = this.receive(this.handler);
					if (temp instanceof PingMessage)
					{
						this.getPingMessage(temp);
					}
					else if (temp instanceof EndSessionAckMessage)
					{
						this.getEndAckSessionMessage(temp);
					}
					else if (temp instanceof WorkRequest)
					{
						WorkRequest wr = (WorkRequest) temp;
						this.log("Info", "Got work request message");
						WorkResponse wrp;
						try
						{
							wrp = wr.process();
							this.send(this.handler, wrp);
							this.log("Info", "Just process work request and sent work response");
						}
						catch (UnknownComputationException e)
						{
							this.log("Error", "Could not process work request: " + wr.getComp());
						}
					}

					else
					{
						this.log("Error", "Received unknown(" + temp.getClass() + " message!");
					}
				}
			}
			catch (IOException e1)
			{
				this.log("Error", "Could not check for new messages:" + e1.getMessage());
			}
			catch (ClassNotFoundException e1)
			{
				this.log("Error", "Could not check for new messages:" + e1.getMessage());
			}

			try
			{
				Thread.sleep(GridConfig.CHECKINTERVAL);
			}
			catch (InterruptedException e)
			{
			}
		}
	}

	@Override
	/**
	 * Closes the worker and sends a message to the activity, though the handler, to close.
	 */
	public void close()
	{
		try
		{
			this.handler.close();
		}
		catch (IOException e)
		{

		}
		this.forceExit = true;
		this.log("Error", "Worker exitting...");
		Message m = Message.obtain();
		m.what = GridConfig.CLOSE;
		this.messageHandler.sendMessage(m);
	}
}
